import java.io.*;
import java.util.*;

//Andy Liang
//a.liang1989@gmail.com

public class PersonListings {
    
    public static class Person{

        String firstname;
        String lastname;
        String middle; //NULL if N/A
        String gender;
        String color;
        String dob;
        
        public Person(String f, String l, String m, String g, String c, String d){
            firstname = f;
            lastname = l;
            middle = m;
            gender = g;
            color = c;
            dob = d;
        }
    }
    
    //the reason why I am using interfaces is to implement a notion of a array
    //of functions (which isn't directly supported in Java). This will reduce the
    //redundancies in the main program (its really just a style thing)
    
    public interface Loader{
        public Person load(String arr[]);
    }
    
    public static class CommaLoader implements Loader{
        public Person load(String arr[]){
            //Last Name, First Name, Gender, Favorite Color, Date Of Birth
            if(arr.length != 5)
                return null;
            Person p_ret = new Person(arr[1], arr[0], null, arr[2], arr[3], arr[4]);
            return p_ret;
        }
    }
    
    public static class PipeLoader implements Loader{
        public Person load(String arr[]){
            //Last Name, First Name, Middle Initial, Gender, Favorite Color, Date Of Birth
            if(arr.length != 6)
                return null;
            if (arr[3].equals("M")) //we are operating under the assumption that gender in pipe.txt is either "M" or "F"
                arr[3] = "Male";
            else
                arr[3] = "Female";
            arr[5] = arr[5].replace('-', '/'); //should probably have used StringBuilder, but there aren't that many strings so it isn't really a big deal
            Person p_ret = new Person(arr[1], arr[0], arr[2], arr[3], arr[4], arr[5]);
            return p_ret;
        }

    }
    
    public static class SpaceLoader implements Loader{
        public Person load(String arr[]){
            //Last Name, First Name, Middle Initial, Gender, Date Of Birth, Favorite Color
            if(arr.length != 6)
                return null;
            if (arr[3].equals("M"))
                arr[3] = "Male";
            else
                arr[3] = "Female";
            arr[4] = arr[4].replace('-', '/');
            Person p_ret = new Person(arr[1], arr[0], arr[2], arr[3], arr[5], arr[4]);
            return p_ret;
        }
    }
    
    //this is in descending order, hence the negative returns
    public static class SortByName implements Comparator <Person> {
        public int compare(Person p1, Person p2){
            int ret = p1.lastname.compareTo(p2.lastname);
            if(ret == 0){
                return -(p1.firstname.compareTo(p2.firstname));
            }
            return -ret;
        }
    }
    
    public static class SortByDoB implements Comparator <Person> {
        public int compare(Person p1, Person p2){
				//probably could have optimized this by converting the string into an integer - however once we get 
				//to the output phase we would have to convert back -- not a huge deal
            String d1[] = p1.dob.split("/");
            String d2[] = p2.dob.split("/");

            for(int i = 2; i >= 0; i--){
                //technically we could have gotten away without converting to an integer,
                //however it behaves oddly if two people have the same birthday if you leave it
                //as a string (it isn't necessarily incorrect however)
                int t1 = Integer.parseInt(d1[2]);
                int t2 = Integer.parseInt(d2[2]);
                if(t1 < t2)
                    return -1;
                if(t1 > t2)
                    return 1;
            }

            //if two people have the same birthday, sort via name. 
            //(for the sake of matching the sample output)
            int ret = p1.lastname.compareTo(p2.lastname);
                if(ret == 0){
                    return p1.firstname.compareTo(p2.firstname);
                }
                return ret;
        }

    }
  
    public static class SortByGender implements Comparator <Person> {
        public int compare(Person p1, Person p2){
            //F < M (alphabetically so we can just work off of that)
            int ret = p1.gender.compareTo(p2.gender);
            if(ret == 0){
                ret = p1.lastname.compareTo(p2.lastname);
                if(ret == 0){
                    return p1.firstname.compareTo(p2.firstname);
                }
                return ret;
            }
            return ret;
        }
    }
    
    //dumps the contents of the ArrayList to standard out (the console)
    public static void printList(ArrayList<Person> list){
        for(int i = 0; i < list.size(); i++){
            Person p = list.get(i);
            System.out.println(p.lastname  + " " + p.firstname + " " + p.gender + " " + p.dob + " " + p.color);
        }
    }
    
    public static void main(String[] args) {
        
        ArrayList <Person> masterList  = new ArrayList <Person> ();
        
        String[] filenames = new String[3];
        filenames[0] = "input_files/comma.txt";
        filenames[1] = "input_files/pipe.txt";
        filenames[2] = "input_files/space.txt";
        
        String[] delimiters = new String[3];
        delimiters[0] = ", ";
        delimiters[1] = " \\| "; //need to use this notation to represent a pipe symbol
        delimiters[2] = " ";
        
        Loader[] loaderArray = new Loader[3];
        loaderArray[0] = new CommaLoader();
        loaderArray[1] = new PipeLoader();
        loaderArray[2] = new SpaceLoader();
        
        try{
            //go in a loop to eliminate redundant lines
            for(int i = 0; i < 3; i++){
                FileInputStream fstream = new FileInputStream(filenames[i]);
                DataInputStream in = new DataInputStream(fstream);
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                String temp;

                while(br.ready()){
                    temp = br.readLine();
                    String arr[] = temp.split(delimiters[i]);
                    Person p = loaderArray[i].load(arr);
                    masterList.add(p);
                }    
            }
        }
        catch(IOException iox){
            System.err.println("IOException: " + iox.getMessage());
        }
        
        Collections.sort(masterList, new SortByGender());
        System.out.println("Output 1:");
        printList(masterList);
        Collections.sort(masterList, new SortByDoB());
        System.out.println("\nOutput 2:");
        printList(masterList);
        Collections.sort(masterList, new SortByName());
        System.out.println("\nOutput 3:");
        printList(masterList);
    }
}

